import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Stack;

public class MainParse {
    static List<String> getInputString() {
        return inputString;
    }

    static void setInputString(List<String> _inputString) {
        inputString = _inputString;
    }

    //输入的词法分析结果
    static List<String> inputString;
    //预测表
    static Map<String, Formula> predictMap;
    //符号栈
    static Stack<String> symbolStack;
    //语法分析结果
    static List<String> parseResultBuf;
    //语法分析计数
    static Integer parseResultCount;
    //语法分析入口函数
    static void DoParse() {
        //传入词法分析结果
        System.out.println(inputString);
        writeLexiconMiddleResultIntoFile();
        System.out.println("成功接收词法分析结果");
        //初始化
        symbolStack = new Stack<>();
        parseResultBuf = new ArrayList<>();
        parseResultCount = 0;

        // 调用Parse，生成各种规则和表
        Parse.Do();
        //获取预测表使用
        predictMap = Parse.predictions;

        //语法分析中间结果写入文件中
        Parse.writeMiddleParseResultIntoFile();

        //开始分析
        System.out.println("开始语法分析----------------------------");
        Parse();
        System.out.println("语法分析结束----------------------------");

        //输出结果
        printParseResult();
    }

    //词法分析结果写入文件
    static void writeLexiconMiddleResultIntoFile() {
        try {
            BufferedWriter bWriter = new BufferedWriter(new FileWriter(Config.lexiconMiddleResult));
            for(String s : inputString) {
                bWriter.write(s + "\n");
            }
            bWriter.close();
        } catch (IOException e) {
            System.out.println("保存词法分析中间结果时出错:" + e.getMessage());
        }
        System.out.println("保存词法分析中间结果成功");
    }

    //语法分析
    static void Parse() {
        //栈底和输入串尾都放上#
        symbolStack.push("#");
        inputString.add("#");
        //初始符号压入栈
        symbolStack.add(Config.initSymbol);
        while (true) {
            //计数器，用于记录输出行号
            parseResultCount++;
            //如果两个栈底都是#表示分析成功
            //成功就退出循环
            parseResultBuf.add("当前栈:" + symbolStack.toString());
            if (symbolStack.peek().equals("#") && inputString.get(0).equals("#")) {
                parseResultBuf.add(parseResultCount + "\t" +
                        "END" + "#" + "END" + "\t" + "accept");
                break;
            }
            //如果栈和队列指向的字符一样，则接受这个字符，弹栈并队列指针向后移动
            if (inputString.get(0).equals(symbolStack.peek())) {
                //记录本次move操作
                parseResultBuf.add(parseResultCount + "\t" +
                        symbolStack.peek() + "#" + inputString.get(0) +
                        "\t" + "move");
                //移动到下一个位置
                inputString.remove(0);
                symbolStack.pop();
                continue;
            }
            //找到当前字符在预测表中对应的文法规则
            String predictMapKey = GeneratePredictMap.getMapKey(inputString.get(0),
                    symbolStack.peek());
            Formula formula = predictMap.get(predictMapKey);
            //文法需要不为空，为空的话就代表出错
            if (formula != null) {
                //找到了说明当前需要的操作是规约
                parseResultBuf.add(parseResultCount + "\t" +
                        symbolStack.peek() + "#" + inputString.get(0) +
                        "\t" + "reduction");
                //符号栈退栈，注意不能退#
                if(!symbolStack.peek().equals("#")) {
                    symbolStack.pop();
                }
                //右侧的生成式字符串
                String[] rights = formula.getRight();
                //逆序推入栈
                //注意若右部符号为ε，则意味着不推任何符号进栈
                if (!rights[0].equals("$")) {
                    //反序插入栈
                   for (int i = rights.length; i > 0; i--) {
                       symbolStack.push(rights[i-1]);
                   }
                }
                parseResultBuf.add("当前栈:" + symbolStack.toString());
            } else {
                //出错
                parseResultBuf.add(parseResultCount + "\t" +
                        symbolStack.peek() + "#" + inputString.get(0) +
                        "\t" + "error");
                return;
            }
        }
        System.out.println("语法分析结束----------------------------");
    }

    //输出结果
    static void printParseResult() {
        System.out.println("输出语法分析结果----------------------------");
        for (String s : parseResultBuf) {
            System.out.println(s);
        }
        //写入文件
        try {
            BufferedWriter bWriter = new BufferedWriter(new FileWriter(Config.parseResultPath));
            for (String s : parseResultBuf) {
                bWriter.write(s + "\n");
            }
            bWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("语法分析结果写入文件成功");
    }
}
